﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using IniTechCaash.Domain.Entities;
using System.IO;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using FluentValidation;

namespace IniTechCaash.WebApplication.Models
{
    #region Create

    public class JournalEntryCreateViewModel : AuditableEntityViewModel
    {
        public DateTime? DatePosted { get; set; }
        public override string Descriptive
        {
            get { return "JNE"; }
        }
        public bool IsPosted { get; set; }
        [StringLength(128)]
        [Required]
        public string Name { get; set; }
        public IList<JournalEntryLineCreateViewModel> Lines { get; set; }
        [DataType(DataType.Upload)]
        public HttpPostedFileBase SourceDocument { get; set; }

        public JournalEntry GetJournalEntry(string userName)
        {
            DateTime timeStamp = DateTime.Now;
            JournalEntry entry = new JournalEntry
            {
                DatePosted = this.DatePosted,
                CreatedBy = userName,
                CreatedDate = timeStamp,
                IsPosted = this.IsPosted,
                LastModifiedBy = userName,
                LastModifiedDate = timeStamp,
                Lines = new List<JournalEntryLine>(this.Lines.Count),
                Name = this.Name
            };
            foreach (var l in Lines)
                entry.Lines.Add(l.GetJournalEntryLine(entry));
            return entry;
        }

        #region Validator
        
        public class Validator : AbstractValidator<JournalEntryCreateViewModel>
        {
            public Validator()
            {
                RuleFor(m => m.Id)
                    .Must((m, id) => {
                        if ((m.Lines.FirstOrDefault(l => l.Side == NormalSide.Debit) == null) || (m.Lines.FirstOrDefault(l => l.Side == NormalSide.Credit) == null))
                            return false;
                        decimal debits = m.Lines.Where(l => l.Side == NormalSide.Debit).Select(l => l.Amount).Aggregate((a, b) => a + b);
                        decimal credits = m.Lines.Where(l => l.Side == NormalSide.Credit).Select(l => l.Amount).Aggregate((a, b) => a + b);
                        return debits == credits;
                    })
                    .WithMessage("Total Debits must equal total Credits.");

                RuleFor(m => m.Id)
                    .Must((m, id) => {
                        if (m.Lines.FirstOrDefault(l => l.Side == NormalSide.Debit) == null)
                            return false;
                        if (m.Lines.FirstOrDefault(l => l.Side == NormalSide.Credit) == null)
                            return false;
                        return true;
                    })
                    .WithMessage("Must have at least one Debit and one Credit.");
            }
        }

        #endregion
    }

    public class JournalEntryLineCreateViewModel : AuditableEntityViewModel
    {
        public int AccountId { get; set; }
        public SelectList AccountSelectList { get; set; }
        [DataType(DataType.Currency)]
        [Range(0.01, Double.MaxValue)]
        [Required]
        public decimal Amount { get; set; }
        [DataType(DataType.Date)]
        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
        [Required]
        public DateTime Date { get; set; }
        [DataType(DataType.MultilineText)]
        [StringLength(256)]
        public string Description { get; set; }
        public override string Descriptive
        {
            get { return "JEL"; }
        }
        public bool IsPosted { get; set; }
        public JournalEntryCreateViewModel JournalEntry { get; set; }
        public NormalSide Side { get; set; }
        //public SourceDocumentCreateViewModel SourceDocument { get; set; }

        public JournalEntryLineCreateViewModel()
        {
            Date = DateTime.Now.Date;
        }

        public JournalEntryLine GetJournalEntryLine(JournalEntry entry)
        {
            return new JournalEntryLine
            {
                Amount = this.Amount,
                CreatedBy = entry.CreatedBy,
                CreatedDate = entry.CreatedDate,
                Date = this.Date,
                Description = this.Description,
                JournalEntry = entry,
                LastModifiedBy = entry.LastModifiedBy,
                LastModifiedDate = entry.LastModifiedDate,
                Side = this.Side,
                //SourceDocument = this.SourceDocument.GetSourceDocument(),
            };
        }
    }

    //public class SourceDocumentCreateViewModel : EntityViewModel
    //{
    //    public FileStream File { get; set;  }
    //    [Required]
    //    public JournalEntryLineCreateViewModel JournalEntryLine { get; set; }
    //    [StringLength(128)]
    //    public string Name { get; set; }

    //    public SourceDocument GetSourceDocument()
    //    {
    //        return new SourceDocument
    //        {
    //            Name = this.Name,
    //        };
    //    }
    //}

    #endregion

    #region Details

    public class JournalEntryDetailsViewModel : AuditableEntityViewModel
    {

        public JournalEntryDetailsViewModel(JournalEntry entry)
        {
            ActivityLogs = new List<ActivityLog>();
            CreatedBy = entry.CreatedBy;
            CreatedDate = entry.CreatedDate;
            DatePosted = entry.DatePosted;
            Id = entry.Id;
            IsPosted = entry.IsPosted;
            LastModifiedBy = entry.LastModifiedBy;
            LastModifiedDate = entry.LastModifiedDate;
            Name = entry.Name;
            Lines = new List<JournalEntryLineDetailsViewModel>(entry.Lines.Count);
            foreach (var l in entry.Lines)
                Lines.Add(new JournalEntryLineDetailsViewModel(l, this));
        }

        public JournalEntryDetailsViewModel()
        {
        }

        public List<ActivityLog> ActivityLogs { get; set; }

        public string CompositeId
        {
            get { return String.Join(" ", DescriptiveId, Name); }
        }
        public DateTime? DatePosted { get; set; }
        public override string Descriptive
        {
            get { return "JNE"; }
        }
        public bool IsPosted { get; set; }
        [StringLength(128)]
        public string Name { get; set; }
        public IList<JournalEntryLineDetailsViewModel> Lines { get; set; }
        public string SourceDocument { get; set; }
    }

    public class JournalEntryLineDetailsViewModel : AuditableEntityViewModel
    {
        public JournalEntryLineDetailsViewModel(JournalEntryLine line, JournalEntryDetailsViewModel parent = null)
        {
            Account = new FinanceAccountDetailsViewModel(line.Account);
            Amount = line.Amount;
            CreatedBy = line.CreatedBy;
            CreatedDate = line.CreatedDate;
            Date = line.Date;
            Description = line.Description;
            Id = line.Id;
            JournalEntry = parent;
            LastModifiedBy = line.LastModifiedBy;
            LastModifiedDate = line.LastModifiedDate;
            Side = line.Side;
        }

        public JournalEntryLineDetailsViewModel()
        {
        }

        public FinanceAccountDetailsViewModel Account { get; set; }
        [DisplayFormat(DataFormatString = "{0:#,###0.00}")]
        public decimal Amount { get; set; }
        public DateTime Date { get; set; }
        [StringLength(256)]
        public string Description { get; set; }
        public override string Descriptive
        {
            get { return "JEL"; }
        }
        public JournalEntryDetailsViewModel JournalEntry { get; set; }
        public NormalSide Side { get; set; }
    }

    //public class SourceDocumentDetailsViewModel : EntityViewModel
    //{
    //    public SourceDocumentDetailsViewModel(SourceDocument doc, JournalEntryLineDetailsViewModel parent = null)
    //    {
    //        JournalEntryLine = parent;
    //        Name = doc.Name;
    //    }

    //    public FileStream File { get; set;  }
    //    [Required]
    //    public JournalEntryLineDetailsViewModel JournalEntryLine { get; set; }
    //    [StringLength(128)]
    //    public string Name { get; set; }
    //}

    #endregion

    #region Edit

    public class JournalEntryEditViewModel : AuditableEntityViewModel
    {
        public DateTime? DatePosted { get; set; }
        public override string Descriptive
        {
            get { return "JNE"; }
        }
        public bool IsPosted { get; set; }
        [StringLength(128)]
        [Required]
        public string Name { get; set; }
        public IList<JournalEntryLineEditViewModel> Lines { get; set; }
        [DataType(DataType.Upload)]
        public HttpPostedFileBase SourceDocument { get; set; }

        public JournalEntryEditViewModel()
        {
            Lines = new List<JournalEntryLineEditViewModel>();
        }

        public JournalEntryEditViewModel(JournalEntry entry)
        {
            CreatedBy = entry.CreatedBy;
            CreatedDate = entry.CreatedDate;
            DatePosted = entry.DatePosted;
            Id = entry.Id;
            IsPosted = entry.IsPosted;
            LastModifiedBy = entry.LastModifiedBy;
            LastModifiedDate = entry.LastModifiedDate;
            Name = entry.Name;
        }

        public JournalEntry GetJournalEntry(JournalEntry entry, string userName)
        {
            DateTime timeStamp = DateTime.Now;

            entry.DatePosted = this.DatePosted;
            entry.Id = this.Id;
            entry.IsPosted = this.IsPosted;
            entry.LastModifiedBy = userName;
            entry.LastModifiedDate = timeStamp;
            entry.Lines = new List<JournalEntryLine>(this.Lines.Count);
            entry.Name = this.Name;

            foreach (var l in Lines)
                entry.Lines.Add(l.GetJournalEntryLine(entry));

            return entry;
        }

        #region Validator
        
        public class Validator : AbstractValidator<JournalEntryEditViewModel>
        {
            public Validator()
            {
                RuleFor(m => m.Id)
                    .Must((m, id) => {
                        if ((m.Lines.FirstOrDefault(l => l.Side == NormalSide.Debit) == null) || (m.Lines.FirstOrDefault(l => l.Side == NormalSide.Credit) == null))
                            return false;
                        decimal debits = m.Lines.Where(l => l.Side == NormalSide.Debit).Select(l => l.Amount).Aggregate((a, b) => a + b);
                        decimal credits = m.Lines.Where(l => l.Side == NormalSide.Credit).Select(l => l.Amount).Aggregate((a, b) => a + b);
                        return debits == credits;
                    })
                    .WithMessage("Total Debits must equal total Credits.");

                RuleFor(m => m.Id)
                    .Must((m, id) => {
                        if (m.Lines.FirstOrDefault(l => l.Side == NormalSide.Debit) == null)
                            return false;
                        if (m.Lines.FirstOrDefault(l => l.Side == NormalSide.Credit) == null)
                            return false;
                        return true;
                    })
                    .WithMessage("Must have at least one Debit and one Credit.");
            }
        }

        #endregion
    }

    public class JournalEntryLineEditViewModel : AuditableEntityViewModel
    {
        public int AccountId { get; set; }
        public SelectList AccountSelectList { get; set; }
        [DataType(DataType.Currency)]
        [Range(0.01, Double.MaxValue)]
        [Required]
        public decimal Amount { get; set; }
        [DataType(DataType.Date)]
        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
        [Required]
        public DateTime Date { get; set; }
        [DataType(DataType.MultilineText)]
        [StringLength(256)]
        public string Description { get; set; }
        public override string Descriptive
        {
            get { return "JEL"; }
        }
        public bool IsPosted { get; set;}
        public JournalEntryEditViewModel JournalEntry { get; set; }
        public NormalSide Side { get; set; }
        //public SourceDocumentCreateViewModel SourceDocument { get; set; }

        public JournalEntryLineEditViewModel(JournalEntryLine line)
        {
            AccountId = line.Account.Id;
            Amount = line.Amount;
            CreatedBy = line.CreatedBy;
            CreatedDate = line.CreatedDate;
            Date = line.Date;
            Description = line.Description;
            Id = line.Id;
            LastModifiedBy = line.LastModifiedBy;
            LastModifiedDate = line.LastModifiedDate;
            Side = line.Side;
        }

        public JournalEntryLineEditViewModel()
        {
        }

        public JournalEntryLine GetJournalEntryLine(JournalEntry entry)
        {
            return new JournalEntryLine
            {
                Amount = this.Amount,
                CreatedBy = entry.CreatedBy,
                CreatedDate = entry.CreatedDate,
                Date = this.Date,
                Description = this.Description,
                Id = this.Id,
                JournalEntry = entry,
                LastModifiedBy = entry.LastModifiedBy,
                LastModifiedDate = entry.LastModifiedDate,
                Side = this.Side,
                //SourceDocument = this.SourceDocument.GetSourceDocument(),
            };
        }
    }

    //public class SourceDocumentEditViewModel : EntityViewModel
    //{
    //    public FileStream File { get; set;  }
    //    [Required]
    //    public JournalEntryLineEditViewModel JournalEntryLine { get; set; }
    //    [StringLength(128)]
    //    public string Name { get; set; }

    //    public SourceDocument GetSourceDocument()
    //    {
    //        return new SourceDocument
    //        {
    //            Id = this.Id,
    //            Name = this.Name,
    //        };
    //    }
    //}

    #endregion

    #region Search

    public class JournalViewModel
    {
        [DataType(DataType.Date)]
        [Display(Name = "End Date")]
        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
        public DateTime? EndDate { get; set; }

        public IEnumerable<JournalEntryDetailsViewModel> JournalEntries { get; set; }

        [Display(Name = "Show Non Posted Entries")]
        public bool ShowNonPosted { get; set; }

        [Display(Name = "Show Posted Entries")]
        public bool ShowPosted { get; set; }

        [DataType(DataType.Date)]
        [Display(Name = "Start Date")]
        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
        public DateTime? StartDate { get; set; }

        public JournalViewModel()
        {
            ShowNonPosted = true;
            ShowPosted = true;
        }
    }

    #endregion
}